**title: Practical JavaScript
** Part 6 - Compatibility and cookies


I love standards, there are so many of them. JavaScript is no exception, so you need to be aware of the different implementations to make sure your web pages work on as many browsers as possible. Later version of JavaScript not only added new features, they also changed the way some of the old ones worked. For example, delete() did one thing in 1.0, was deprecated from 1.1 and came back for 1.2 with a different action (it's best to avoid this one). Other features, including == and some of the Date methods, vary between versions. The solution is to use the lowest version of JavaScript that supports all the functions you need, and specify that in the <SCRIPT> tag. For example, although == works different in 1.1 and 1.2, it should always work in the correct way for whichever version you give in <SCRIPT>.

A more serious compatibility issue is the differing implementation of the same JavaScript version by browser manufacturers. The different approaches to case-sensitivity was covered in Part 1, but there can be other surprises. As with HTML, the only reliable option is to test your pages and scripts in as many different browsers as possible, and make sure the pages are still usable if JavaScript is not available.





**xhead: Anyone for cookies?

Cookies have something of a bad name. This has largely come about because people don't understand what they do, so they believe the worst of the scare stories. The same attitude gave comets the blame for anything that went wrong in the Middle Ages. A cookie is simply data storage, the server gives your browser a piece of information and the browser passes it back when you next visit the site. Since the information comes from the server, the cookie can't contain any information the server doesn't already have. It's like an environment variable, where a program stores your settings when it exits so it can use them again the next time you run it.

Cookies were originally intended for CGI scripts, but JavaScript can use them too. Here is an example that could be called from any part of a page's body text:

<script type="text/javascript" language="JavaScript1.1">
<!--
function DateCookie()
{
Cookies = document.cookie.split(';');

for (var i = 0; i < Cookies.length; i++)
{
if (Cookies[i].indexOf('LastVisit=') == 0)
{
LastVisit = unescape(Cookies[i].substring(10,Cookies[i].length));
break;
}
}

if (LastVisit == 'undefined')
document.write('This is your first visit here. Welcome!<br>');
else
document.write('Welcome back, your last visit was on ' + LastVisit + '<br>');

document.cookie = 'LastVisit=' + escape(Date());
}
// -->
</script>

All the cookies relating to a document are stored in the single property, document.cookie. They are stored in the form "name1=value1;name2=value2;..." The first line of the function splits the list of cookies so that each name=value pair is stored as an array element. The for loop runs through the array looking for the one called LastVisit. If it finds it, the value is assigned to a variable and we use break to exit the loop.

Cookies may not contain spaces or non-alphanumeric characters. We use the JavaScript functions escape() and unescape() to translate to and from the standard URL encoding scheme where a character with the hexadecimal ASCII code of XX is represented as %XX. We then test to see if the variable has been set up and display the appropriate message. Finally, we resave the cookie with the current date and time. When we set a value for document.cookie, that cookie is added to the list of cookies for that page. If it already exists, it is updated.

When you first load this into a browser, you'll see the "Welcome" message, reload and you'll see "Welcome back". However, if you quit and restart your browser, you will see the "Welcome" message again, what happened to the cookie? By default, cookies are transient, otherwise known as session cookies. They exist only in the browser's memory and disappear when it exits. We create a persistent cookies by giving it an expiry date. Replace the last line with:

Now = new Date();
Exp = new Date(Now.getYear() + 1, Now.getMonth, Now.getDate());
document.cookie = 'LastVisit=' + escape(Now) + ';Expires=' + escape(Exp);

The first line creates a new object of type date. The Date() function has no arguments, so this is set to the current date and time. The next line specifies the year, month and day, taken from the current date with 1 added to the year. This means the cookie will expire one year from today. Then we add the expiry date to the cookie. Now you can quit the browser, even reboot, and the last date will still be remembered.



**xhead: Using cookie data

So, we can tell a visitor when he was last here, that's hardly Earth-shattering, let's do something a bit more useful. First, we can add the facility for the page to address the visitor by name. We change the loop that reads the cookies so that it will identify every cookie in the list and store each in a variable


for (var i = 0; i < Cookies.length; i++)
{
CookieName = Cookies[i].substring(0,Cookies[i].indexOf('='));
CookieValue = unescape(Cookies[i].substring(Cookies[i].indexOf('=') + 1,Cookies[i].length));
eval(CookieName + '="' + CookieValue + '"');
}
SetCookie('LastVisit',Now);

This uses substring() to split each Name=Value pair at the equals sign and eval() to assign the value to a variable of the same name as the cookie. Then we update the value of the LastVisit cookie. To save calculating the full cookie string, including expiry date, each time, we'll use this function to set cookie values:

function SetCookie(CookieName,CookieValue)
{
document.cookie = CookieName + '=' + escape(CookieValue) + ';Expires=' + escape(Exp);
}



if (Name == 'undefined')
Name = 'stranger';

This sets a substitute Name if the user doesn't want to give their own. We'll be using Name later on, so it needs a value.

if (LastVisit == 'undefined')
{
document.write('This is your first visit here. Welcome!');
document.write('<p><form name="NameForm" onSubmit="SetName();return false">Please enter your name here: <input type="text" name="YourName"></form>');
}

If it's the first visit, we write a one field form to the page for the visitor to give their name. The form calls this function to store the name in both a variable and a cookie.

function SetName()
{
if (document.NameForm.YourName.value > '')
{
Name = document.NameForm.YourName.value;
SetCookie('Name',Name);
}
}



Now we can greet the user by name each time they call, but we can do more. We know when they last visited this page, we also know when it was last changed, it was one of the first things covered in Part 1. So we can compare the two and let the visitor know if there have been updates since he was last here.

if (LastVisit == 'undefined')
{
// as above
}
else
{
document.write('Welcome back ' + Name + '. ');
if (Date.parse(LastVisit) < Date.parse(document.lastModified))
document.write('This site has seen some changes since you were last here on ' + LastVisit + '. Click <a href="changes.html">here to see them</a>.<p>');
else
document.write('There have been no changes to this site since your last visit.<p>');

if (Name == 'stranger')
{
document.write('You did not leave your name last time you called. I don\'t want to appear rude, so can you please enter your name in the box?');
document.write('<p><form name="NameForm" onSubmit="SetName();return false">Please enter your name here: <input type="text" name="YourName"></form>');
}
}

A date is normally represented as a string of the form "Fri, 14 Jan 2000 13:58:55 GMT". We cannot compare two dates like this, "Fri, 13" would be considered less than "Thu, 12". Date.parse() converts a date from its string representation into the number of milliseconds since !st January 1970. The first if statement above compares the date of the last visit with the date this page last changed. If the last visit was earlier, it inserts an appropriate message along with a link to the page containing a list of changes. For this to work, the page containing the script must be updated whenever any changes are made to the site. All you need to do to alter the document.lastModified date is upload this page to the server again. See name.html on the CD for the full script.


**xhead: Limiting the number of cookies

The RFCs give some limits on the number and size of cookies, you don't want a site filling your drive with them and slowing down your connection as they are transferred. The main one to be aware of is that a browser is only required to store twenty cookies per server (not per page) and cookies should not exceed 4KB in size. The twenty cookie limit could cause problems, so it is best to store multiple values in a single cookie. We could store the name and last visited date in a single cookie, "VisitData=Name|Date", use indexOf() to split it on the "|".

If you want to delete a cookie, set it with any value and an expiry date in the past. Here's a function to do it. Browsers don't always clear out expired cookies immediately, so don't rely on this happening.

function DelCookie(CookieName)
{
OldExp = new Date(Now.getYear() - 1, Now.getMonth, Now.getDate());
document.cookie = CookieName + '=xyz;Expires=' + escape(OldExp);
}



You could use a similar process to let a user choose between low and high bandwidth versions of your site the first time they connect, and taking them automatically to their choice on each subsequent connect. I hope this series has given you a few ideas of your own, here are many ways to enhance your web site with JavaScript.



** Browser.png: What a difference a few lines of JavaScript make, the site is now personalised to each visitor